package youdu

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"crypto/sha1"
	"encoding/base64"
	"encoding/binary"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"sort"
	"strings"
)

const PADDING = 32

type rawMsg struct {
	Data   []byte
	Length int32
	AppId  string
}

func aesPadding(in []byte) []byte {
	padding := PADDING - (len(in) % PADDING)
	if padding == 0 {
		padding = PADDING
	}
	for i := 0; i < padding; i++ {
		in = append(in, byte(padding))
	}
	return in
}

func aesUnpadding(in []byte) []byte {
	if len(in) == 0 {
		return nil
	}

	padding := in[len(in)-1]
	if int(padding) > len(in) || padding > PADDING {
		return nil
	} else if padding == 0 {
		return nil
	}

	for i := len(in) - 1; i > len(in)-int(padding)-1; i-- {
		if in[i] != padding {
			return nil
		}
	}
	return in[:len(in)-int(padding)]
}

func (o *tools) AesEncrypt(text []byte, key []byte, appId string) (string, error) {
	all := make([]byte, 0)
	randBs := make([]byte, 16)
	io.ReadFull(rand.Reader, randBs)

	lenBs := make([]byte, 4)
	binary.BigEndian.PutUint32(lenBs, uint32(len(text)))

	all = append(all, randBs...)        // 16 rand bytes
	all = append(all, lenBs...)         // 4 length bytes
	all = append(all, []byte(text)...)  // msg content
	all = append(all, []byte(appId)...) // appId content

	enBs, err := aesEncrypt(all, key)
	if err != nil {
		return "", err
	}
	return base64.StdEncoding.EncodeToString(enBs), nil
}

func aesEncrypt(data []byte, key []byte) ([]byte, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}

	iv := make([]byte, 16)
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		return nil, err
	}

	data = aesPadding(data)
	blockMode := cipher.NewCBCEncrypter(block, iv)
	enBs := make([]byte, len(data))
	blockMode.CryptBlocks(enBs, data)
	return enBs, nil
}

func (o *tools) AesDecrypt(encText string, key []byte, appId string) (*rawMsg, error) {
	cipherData, err := base64.StdEncoding.DecodeString(encText)
	if err != nil {
		return nil, err
	}

	rawData, err := aesDecrypt(cipherData, key)
	if err != nil {
		return nil, err
	}
	if len(rawData) <= 20 {
		return nil, errors.New("error content length")
	}

	m := &rawMsg{}
	binary.Read(bytes.NewBuffer(rawData[16:20]), binary.BigEndian, &m.Length)
	m.Data = rawData[20 : 20+m.Length]
	m.AppId = string(rawData[20+m.Length:])
	if len(rawData) < int(20+m.Length) {
		return nil, errors.New("error length content")
	}
	return m, nil
}

func aesDecrypt(data []byte, key []byte) ([]byte, error) {
	keyLen := len(key)
	if len(data)%keyLen != 0 {
		return nil, errors.New("ciphertext size is not multiple of aes key length")
	}

	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}

	iv := make([]byte, aes.BlockSize)
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		return nil, err
	}
	blockMode := cipher.NewCBCDecrypter(block, iv)
	rawData := make([]byte, len(data))
	blockMode.CryptBlocks(rawData, data)
	rawData = aesUnpadding(rawData)
	if rawData == nil {
		return nil, errors.New("unpadding failed")
	}
	return rawData, nil
}

// import (
// 	"bytes"
// 	"crypto/aes"
// 	"crypto/cipher"
// 	"crypto/rand"
// 	"crypto/sha1"
// 	"encoding/base64"
// 	"encoding/binary"
// 	"encoding/hex"
// 	"fmt"
// 	"sort"
// 	"strings"
// )

// // AESEncrypt AES加密
// // plaintext: 明文
// // aesKey: AES密钥
// // appId: 应用ID
// func AESEncrypt(plaintext []byte, aesKey []byte, appId string) (string, error) {
// 	// 生成16字节随机字符串
// 	randBytes := make([]byte, 16)
// 	if _, err := rand.Read(randBytes); err != nil {
// 		return "", fmt.Errorf("生成随机字符串失败: %v", err)
// 	}

// 	// 计算明文长度，网络字节序
// 	var msgLen [4]byte
// 	binary.BigEndian.PutUint32(msgLen[:], uint32(len(plaintext)))

// 	// 拼接最终需要加密的数据
// 	// randBytes(16B) + msgLen(4B) + plaintext + appId
// 	buffer := bytes.NewBuffer(randBytes)
// 	buffer.Write(msgLen[:])
// 	buffer.Write(plaintext)
// 	buffer.WriteString(appId)
// 	padMsg := buffer.Bytes()

// 	// 对明文进行PKCS7填充
// 	blockSize := aes.BlockSize
// 	padding := blockSize - len(padMsg)%blockSize
// 	padText := bytes.Repeat([]byte{byte(padding)}, padding)
// 	padMsg = append(padMsg, padText...)

// 	// 创建AES-CBC加密器
// 	block, err := aes.NewCipher(aesKey)
// 	if err != nil {
// 		return "", fmt.Errorf("创建AES加密器失败: %v", err)
// 	}

// 	// 使用随机IV
// 	ciphertext := make([]byte, aes.BlockSize+len(padMsg))
// 	iv := ciphertext[:aes.BlockSize]
// 	if _, err := rand.Read(iv); err != nil {
// 		return "", fmt.Errorf("生成IV失败: %v", err)
// 	}

// 	mode := cipher.NewCBCEncrypter(block, iv)
// 	mode.CryptBlocks(ciphertext[aes.BlockSize:], padMsg)

// 	// Base64编码
// 	return base64.StdEncoding.EncodeToString(ciphertext), nil
// }

// // AESDecrypt AES解密
// // ciphertext: 密文
// // aesKey: AES密钥
// // appId: 应用ID，用于校验
// func AESDecrypt(encryptMsg string, aesKey []byte, appId string) ([]byte, error) {
// 	// Base64解码
// 	ciphertext, err := base64.StdEncoding.DecodeString(encryptMsg)
// 	if err != nil {
// 		return nil, fmt.Errorf("base64解码失败: %v", err)
// 	}

// 	block, err := aes.NewCipher(aesKey)
// 	if err != nil {
// 		return nil, fmt.Errorf("创建AES解密器失败: %v", err)
// 	}

// 	if len(ciphertext) < aes.BlockSize {
// 		return nil, fmt.Errorf("密文长度不足")
// 	}

// 	iv := ciphertext[:aes.BlockSize]
// 	ciphertext = ciphertext[aes.BlockSize:]

// 	// CBC模式解密
// 	mode := cipher.NewCBCDecrypter(block, iv)
// 	mode.CryptBlocks(ciphertext, ciphertext)

// 	// 去除PKCS7填充
// 	padding := int(ciphertext[len(ciphertext)-1])
// 	ciphertext = ciphertext[:len(ciphertext)-padding]

// 	// 解析解密后的数据
// 	if len(ciphertext) < 20 { // 至少要包含16B随机字符串+4B消息长度
// 		return nil, fmt.Errorf("解密后数据长度不正确")
// 	}

// 	// 跳过16B随机字符串
// 	msgLen := binary.BigEndian.Uint32(ciphertext[16:20])
// 	if len(ciphertext) < 20+int(msgLen)+len(appId) {
// 		return nil, fmt.Errorf("解密后数据长度不匹配")
// 	}

// 	// 验证appId
// 	if string(ciphertext[20+msgLen:]) != appId {
// 		return nil, fmt.Errorf("AppID不匹配")
// 	}

// 	// 返回实际消息内容
// 	return ciphertext[20 : 20+msgLen], nil
// }

// GenerateSignature 生成消息签名
// token: 应用回调设置的token
// timestamp: 时间戳
// nonce: 随机字符串
// msgEncrypt: 加密的消息体
func (o *tools) GenerateSignature(token, timestamp, nonce, msgEncrypt string) string {
	// 按字典序排序
	params := []string{token, timestamp, nonce, msgEncrypt}
	sort.Strings(params)

	// 拼接字符串
	str := strings.Join(params, "")

	// SHA1计算
	h := sha1.New()
	h.Write([]byte(str))

	return hex.EncodeToString(h.Sum(nil))
}

// DecodeAESKey 将Base64编码的AESKey解码为原始字节
func (o *tools) DecodeAESKey(encodedAESKey string) ([]byte, error) {
	aesKey, err := base64.StdEncoding.DecodeString(encodedAESKey)
	if err != nil {
		return nil, fmt.Errorf("AESKey解码失败: %v", err)
	}
	return aesKey, nil
}
